/*
 * HexBase.java
 *
 * Created on November 15, 2007, 7:31 PM
 *
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package hextd.hex;

import hextd.HexTD.GameState;
import hextd.UI.ScaledCanvas;
import hextd.core.GameGrid;
import hextd.hex.Hex.highlightType;
import hextd.tower.Tower;
import hextd.core.Context;
import hextd.core.ContextNormal;
import hextd.util.Geometry;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.EnumMap;

/**
 *
 * @author hylke
 */
public class HexBase implements Hex {

	public static char key = 'b';
	protected char shortName;
	protected Hex.HexType type;
	protected int x;
	protected int y;
	//
	protected float paintScale, strokeScale;
	protected float offsetX, offsetY;
	protected float xFloat, yFloat;
	protected float xPaint, yPaint;
	protected float wPaint, hPaint;
	protected boolean leftToRight = true;
	//
	private float[][] points;
	//
	protected Shape polyHex;
	protected Stroke stroke1;
	protected Stroke stroke2;
	protected Shape[] bordersE;
	protected Color borderColor;
	protected Color borderEmptyColor;
	protected Color fillColor;
	protected Color highlightOK;
	protected Color highlightNOK;
	private Color rangeColor;
	//
	protected Context context;
	protected ContextNormal cn;
	protected highlightType highlight = highlightType.none;
	private float highlightRange = 0;
	private Shape rangeCircle;
	private boolean scaled = false;
	//
	private boolean free = true;
	private Tower tower = null;
	//
	private EnumMap<Hex.neighbours, Hex> neighbours = new EnumMap<neighbours, Hex>(Hex.neighbours.class);
	private int[] claims = new int[2];
	private int claimPlayer = -1;

	/** Creates a new instance of HexBase */
	public HexBase(int x, int y, Context context, boolean buildable, boolean leftToRight) {
		Color tempColor;
		this.context = context;
		this.leftToRight = leftToRight;
		this.setLocation(x, y);
		if (context instanceof ContextNormal) {
			cn = (ContextNormal) context;
			this.borderColor = cn.colors.getColorBaseHexBorder();
			tempColor = cn.colors.getColorBaseHexBackGround();
			this.fillColor = new Color(tempColor.getRed(), tempColor.getGreen(), tempColor.getBlue(), 125);
			this.borderEmptyColor = cn.colors.getColorBaseEmptyHexBorder();
			tempColor = cn.colors.getColorHighlightOK();
			this.highlightOK = new Color(tempColor.getRed(), tempColor.getGreen(), tempColor.getBlue(), 125);
			tempColor = cn.colors.getColorHighlightNOK();
			this.highlightNOK = new Color(tempColor.getRed(), tempColor.getGreen(), tempColor.getBlue(), 125);
			this.rangeColor = cn.colors.getColorRangeCircle();
		}
		this.type = HexType.base;
		this.shortName = key;
		this.free = buildable;
	}

	@Override
	public void setLocation(int x, int y) {
		this.x = x;
		this.y = y;
		this.setLocation();
	}

	@Override
	public int[] getLocation() {
		int[] retVal = new int[2];
		retVal[0] = x;
		retVal[1] = y;
		return retVal;
	}

	@Override
	public int getCol() {
		return x;
	}

	@Override
	public int getRow() {
		return y;
	}

	@Override
	public float[] getFloatLocation() {
		float[] returnVal = new float[2];
		returnVal[0] = this.xFloat;
		returnVal[1] = this.yFloat;
		return returnVal;
	}

	@Override
	public float[] getPaintLocation() {
		float[] returnVal = new float[2];
		returnVal[0] = this.xPaint;
		returnVal[1] = this.yPaint;
		return returnVal;
	}

	@Override
	public boolean hasTower() {
		return (this.tower != null);
	}

	@Override
	public void unSetTower() {
		if (this.tower != null) {
			this.tower = null;
			this.free = true;
		}
	}

	@Override
	public void setTower(Tower tower) {
		if (this.free) {
			this.tower = tower;
			this.free = false;
		}
	}

	@Override
	public Tower getTower() {
		return this.tower;
	}

	@Override
	public void setHighlight(highlightType highlight) {
		this.highlight = highlight;
	}

	@Override
	public void setHighlightRange(float range) {
		this.highlightRange = range;
		float paintRange = this.highlightRange * paintScale;
		float xPaintCircle = offsetX + this.xFloat * this.paintScale;
		float yPaintCircle = offsetY + this.yFloat * this.paintScale;
		this.rangeCircle = new Ellipse2D.Float(xPaintCircle - paintRange, yPaintCircle - paintRange, paintRange * 2, paintRange * 2);
	}

	@Override
	public void neighbours(int radius, ArrayList<Hex> collection) {
		if (!collection.contains(this)) {
			collection.add(this);
		}
		if (radius > 0) {
			for (Hex neighbour : this.neighbours.values()) {
				if (neighbour != null) {
					neighbour.neighbours(radius - 1, collection);
				}
			}
		}
	}

	@Override
	public void initNeighbours() {
		GameGrid gameGrid = context.getGameGrid();
		this.neighbours.put(Hex.neighbours.right, gameGrid.getNeightbourOf(x, y, Hex.neighbours.right));
		this.neighbours.put(Hex.neighbours.left, gameGrid.getNeightbourOf(x, y, Hex.neighbours.left));
		this.neighbours.put(Hex.neighbours.rightUp, gameGrid.getNeightbourOf(x, y, Hex.neighbours.rightUp));
		this.neighbours.put(Hex.neighbours.leftUp, gameGrid.getNeightbourOf(x, y, Hex.neighbours.leftUp));
		this.neighbours.put(Hex.neighbours.rightDown, gameGrid.getNeightbourOf(x, y, Hex.neighbours.rightDown));
		this.neighbours.put(Hex.neighbours.leftDown, gameGrid.getNeightbourOf(x, y, Hex.neighbours.leftDown));
	}

	private void setLocation() {
		float[] loc = context.getGameGrid().getHexLocation(x, y);
		xFloat = loc[0];
		yFloat = loc[1];
	}

	@Override
	public void resetScale() {
		ScaledCanvas sc = cn.getScaledCanvas();
		paintScale = sc.getPaintScale();
		strokeScale = sc.getStrokeScale();
		offsetX = sc.getOffsetX();
		offsetY = sc.getOffsetY();

		xPaint = offsetX + xFloat * paintScale;
		yPaint = offsetY + yFloat * paintScale;

		points = Geometry.createHexCoords(paintScale, xPaint, yPaint, leftToRight);
		polyHex = Geometry.coordsToGeneralPath(points, true);

		ArrayList<Shape> borders = new ArrayList<Shape>();
		if (this.neighbours.get(Hex.neighbours.right) != null && this.neighbours.get(Hex.neighbours.right).getType() == HexType.path) {
			// Right is empty
			Line2D tempLine = new Line2D.Float(points[1][0], points[1][1], points[2][0], points[2][1]);
			borders.add(tempLine);
		}
		if (this.neighbours.get(Hex.neighbours.rightUp) != null && this.neighbours.get(Hex.neighbours.rightUp).getType() == HexType.path) {
			// Right-Up is empty
			Line2D tempLine = new Line2D.Float(points[0][0], points[0][1], points[1][0], points[1][1]);
			borders.add(tempLine);
		}

		if (this.neighbours.get(Hex.neighbours.rightDown) != null && this.neighbours.get(Hex.neighbours.rightDown).getType() == HexType.path) {
			// Right-Down is empty
			Line2D tempLine = new Line2D.Float(points[2][0], points[2][1], points[3][0], points[3][1]);
			borders.add(tempLine);
		}


		if (this.neighbours.get(Hex.neighbours.left) != null && this.neighbours.get(Hex.neighbours.left).getType() == HexType.path) {
			// Left is empty
			Line2D tempLine = new Line2D.Float(points[4][0], points[4][1], points[5][0], points[5][1]);
			borders.add(tempLine);
		}

		if (this.neighbours.get(Hex.neighbours.leftUp) != null && this.neighbours.get(Hex.neighbours.leftUp).getType() == HexType.path) {
			// Left-Up is empty
			Line2D tempLine = new Line2D.Float(points[5][0], points[5][1], points[0][0], points[0][1]);
			borders.add(tempLine);
		}
		if (this.neighbours.get(Hex.neighbours.leftDown) != null && this.neighbours.get(Hex.neighbours.leftDown).getType() == HexType.path) {
			// Left-Down is empty
			Line2D tempLine = new Line2D.Float(points[3][0], points[3][1], points[4][0], points[4][1]);
			borders.add(tempLine);
		}

		this.bordersE = new Shape[borders.size()];
		this.bordersE = borders.toArray(this.bordersE);

		this.stroke1 = new BasicStroke(1.0f * strokeScale, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL);
		this.stroke2 = new BasicStroke(3.0f * strokeScale, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

		float paintRange = this.highlightRange * this.paintScale;
		this.rangeCircle = new Ellipse2D.Float(xPaint - paintRange, yPaint - paintRange, paintRange * 2, paintRange * 2);
		this.scaled = true;
	}

	public void paintRangeCircle(Graphics2D g2) {
		g2.setColor(this.rangeColor);
		g2.draw(this.rangeCircle);
	}

	@Override
	public void paintBase(Graphics2D g2_0, Graphics2D g2_1) {
		if (this.scaled) {
			if (g2_1 != null && this.scaled) {
				Stroke defaultStroke = g2_1.getStroke();

				g2_1.setStroke(this.stroke1);
				g2_1.setColor(this.fillColor);
				if (this.claimPlayer >= 0) {
					g2_1.setColor(cn.colors.getColorPlayerAlpha(this.claimPlayer));
				}
				g2_1.fill(this.polyHex);
				g2_1.setColor(this.borderColor);
				g2_1.draw(this.polyHex);

				g2_1.setStroke(this.stroke2);
				g2_1.setColor(this.borderEmptyColor);
				for (int i = 0; i < this.bordersE.length; i++) {
					g2_1.draw(this.bordersE[i]);
				}

				g2_1.setStroke(defaultStroke);
			}
		}
	}

	@Override
	public void paintEffects(Graphics2D g2) {
		if (this.scaled) {
			switch (this.highlight) {
				case placeTower:
					if (this.isBuildable(context.getLocalPlayerId())) {
						g2.setColor(this.highlightOK);
						this.paintRangeCircle(g2);
					} else {
						g2.setColor(this.highlightNOK);
						this.paintRangeCircle(g2);
					}
					g2.fill(this.polyHex);
					break;
				case editHex:
					g2.setColor(this.highlightOK);
					g2.fill(this.polyHex);
					break;
				case selectTower:
					g2.setColor(this.highlightOK);
					g2.fill(this.polyHex);
					break;
			}
		}
	}

	@Override
	public char getShortName() {
		return shortName;
	}

	@Override
	public HexType getType() {
		return type;
	}

	@Override
	public boolean isBuildable(int player) {
		if (free) {
			if (player == this.claimPlayer ||
					this.context.getGameState() == GameState.spPlaying ||
					this.context.getGameState() == GameState.replaying ||
					this.context.getGameState() == GameState.checking) {
				return true;
			}
		}
		return false;
	}

	@Override
	public void addClaim(int player, int level) {
		switch (context.getGameState()) {
			case spPlayingWaveless:
			case mpPlaying:
			case mpWatching:
			case mpChecking:
			case mpCatchup:
				if (player >= 0) {
					if (player >= claims.length) {
						int[] newClaims = new int[player + 1];
						System.arraycopy(claims, 0, newClaims, 0, claims.length);
						claims = newClaims;
					}
					claims[player] += level;
					if (hasTower() && tower.isActive()) {
						claimPlayer = tower.getOwner();
					} else {
						int highest = 0;
						claimPlayer = -1;
						for (int i = 0; i < claims.length; i++) {
							if (claims[i] > highest) {
								highest = claims[i];
								claimPlayer = i;
							} else if (claims[i] == highest) {
								claimPlayer = -1;
							}
						}
						if (hasTower() && !tower.isActive()) {
							if (claimPlayer != tower.getOwner()) {
								System.out.println("HexBase::addClaim: Hex (" + x + "," + y + ") claimed by player " + claimPlayer +
										" before tower of player " + tower.getOwner() + " activated. Cancelling build.");
								tower.cancelBuild();
							}
						}
					}
					context.hexChanged();
				}
		}
	}

	@Override
	public void addClaim(int player, int level, int radius) {
		ArrayList<Hex> hexes = new ArrayList<Hex>(19);
		this.neighbours(radius, hexes);
		for (Hex hex : hexes) {
			hex.addClaim(player, level);
		}
	}
}
